#include <EEPROM.h>
#include <array>

#define CONFIG_MAGIC_NUM 0xFFEE
#define MAX_SSID_SIZE 32
#define MAX_PSW_SIZE 63
#define MAX_TRY_COUNT 10
#define AUTOCFGSSID "ESP8266_WIFI"
#define AUTOCFGPSW "12345678"
// 保存和读取配置
class CConfigMgr
{
    bool m_bMotify = false;

    enum ID : uint8_t
    {
        ID_SSID = 0,
        ID_PSW,
        ID_KEY,
        ID_TOPIC
    };
    // 如果数据比较多应该做成 size|type|data的方式，这样就可以使用通用方式存储和读取所有数据
    std::array<String, 4> m_data;

    void GetData(String &out, const uint8_t **psrc, uint8_t max_size = 0xFF)
    {
        uint8_t stringsize = (*psrc)[0];
        (*psrc)++;
        if (stringsize > 0 && stringsize <= max_size)
        {
            char *pout = new char[stringsize + 1];
            memcpy(pout, (*psrc), stringsize);
            pout[stringsize] = '\0';
            out = pout;
            delete[] pout;
            *psrc += stringsize;
        }
    }

    void PutData(const String &data, uint8_t **pdata)
    {
        (*pdata)[0] = (uint8_t)data.length();
        (*pdata)++;
        memcpy(*pdata, data.begin(), data.length());       
        *pdata += data.length();
    }

    void PrintEEPROM(const uint8_t *pEEPROM, const uint8_t *pEnd)
    {
        for (; pEEPROM < pEnd;)
        {
            uint8_t stringsize = pEEPROM[0];
            pEEPROM++;
            char *pout = new char[stringsize + 1];
            memcpy(pout, pEEPROM, stringsize);
            pout[stringsize] = '\0';
            Serial.println(stringsize);
            Serial.println(pout);
            delete[] pout;
            pEEPROM += stringsize;
        }
    }

    uint16_t GetDataSize() const
    {
        uint16_t needsize = m_data.size();
        for (auto const &ite : m_data)
        {
            needsize += ite.length();
        }
        return needsize;
    }

#define CHECKMEM_AND_RETURN(cur, end, ret) \
    if (cur > end)                         \
        return ret;

    // 从EEPROM读取配置文件addr为保存配置文件地址
    bool Init()
    {
        /*
        uint16_t   uint16_t     content----------------------content---------
        magic_num  config_size  ssid psw other------------uint8_t--ssidsize ssid uint8_t--pswsize password
        */
        Serial.println("开始读取配置文件");
        uint16_t magic_num;
        EEPROM.begin(sizeof(magic_num));
        if (EEPROM.get(0, magic_num) != CONFIG_MAGIC_NUM)
        {
            Serial.println("非法的配置文件");
            return false;
        }
        // 正常的配置文件读取配置文件尺寸
        uint16_t config_size = 0;
        EEPROM.begin(sizeof(uint16_t) * 2);
        EEPROM.get(sizeof(uint16_t), config_size);
        if (config_size > 0)
        {
            // 真实尺寸
            EEPROM.begin(sizeof(uint16_t) * 2 + config_size);
            const uint8_t *pCur = EEPROM.getConstDataPtr() + sizeof(uint16_t) * 2;
            const uint8_t *pEnd = pCur + config_size;

            GetData(m_data[ID_SSID], &pCur, MAX_SSID_SIZE);
            CHECKMEM_AND_RETURN(pCur, pEnd, true);
            GetData(m_data[ID_PSW], &pCur, MAX_PSW_SIZE);
            CHECKMEM_AND_RETURN(pCur, pEnd, true);
            GetData(m_data[ID_KEY], &pCur, 32);
            CHECKMEM_AND_RETURN(pCur, pEnd, true);
            GetData(m_data[ID_TOPIC], &pCur);
            CHECKMEM_AND_RETURN(pCur, pEnd, true);

            EEPROM.end();

            Serial.println("配置设置：ssid--" + m_data[ID_SSID] +
                           "--|--psw--" + m_data[ID_PSW] +
                           "--|--key--" + m_data[ID_KEY] +
                           "--|--topic--" + m_data[ID_TOPIC]);

            return true;
        }
        return false;
    }

    void SetData(String &des, const char *src)
    {
        if (src)
        {
            des = src;
        }
        else
        {
            des.clear();
        }
    }

    CConfigMgr()
    {
        Init();
    }

public:
    static CConfigMgr &getSingleton()
    {
        static CConfigMgr _singleton;
        return _singleton;
    }

    ~CConfigMgr()
    {
        if (m_bMotify)
        {
            SaveConfig();
        }
    }

    void SaveConfig()
    {
        Serial.println("保存设置：ssid--" + m_data[ID_SSID] +
                       "--|--psw--" + m_data[ID_PSW] +
                       "--|--key--" + m_data[ID_KEY] +
                       "--|--topic--" + m_data[ID_TOPIC]);

        uint16_t needsize = GetDataSize();
        uint16_t magic_num = CONFIG_MAGIC_NUM;

        EEPROM.begin(sizeof(uint16_t) * 2 + needsize);

        EEPROM.put(0, magic_num);
        EEPROM.put(sizeof(uint16_t), needsize);

        uint8_t *pCur = EEPROM.getDataPtr();
        pCur += sizeof(uint16_t) * 2;

        for (auto const &ite : m_data)
        {
            PutData(ite, &pCur);
        }

        EEPROM.end();
    }

    void SetSSID(const char *SSID)
    {
        SetData(m_data[ID_SSID], SSID);
        m_bMotify = true;
    }

    void SetPSW(const char *PSW)
    {
        SetData(m_data[ID_PSW], PSW);
        m_bMotify = true;
    }

    void SetKEY(const char *KEY)
    {
        SetData(m_data[ID_KEY], KEY);
        m_bMotify = true;
    }

    void SetTOPIC(const char *TOPIC)
    {
        SetData(m_data[ID_TOPIC], TOPIC);
        m_bMotify = true;
    }

    const String &SSID() const
    {
        return m_data[ID_SSID];
    }

    const String &PSW() const
    {
        return m_data[ID_PSW];
    }

    const String &KEY() const
    {
        return m_data[ID_KEY];
    }

    const String &TOPIC() const
    {
        return m_data[ID_TOPIC];
    }

    bool IsWifiInfoValid() const
    {
        return !(m_data[ID_SSID].isEmpty() || m_data[ID_PSW].isEmpty());
    }
};